#include "PageCache.h"

Span *PageCache::NewSpan(size_t numpage)
{
    if (!spanLists[numpage].Empty()) {
        Span *span = spanLists[numpage].Begin();
        spanLists[numpage].PopFront();
        for(size_t i=0;i<numpage;i++){
            idSpanMap[span->pageid+i] = span;
        }
        return span;
    }

    for (size_t i = numpage + 1; i < MAX_PAGES; ++i) {
        if (!spanLists[i].Empty()) {
            Span *span = spanLists[i].Begin();
            spanLists[i].PopFront();

            Span *splitspan = pool.New();
            splitspan->pageid = span->pageid;
            splitspan->pagenums = numpage;

            span->pageid += numpage;
            span->pagenums -= numpage;

            idSpanMap[span->pageid] = span;
            idSpanMap[span->pageid + span->pagenums - 1] = span;
            for (PAGE_ID i = 0; i < numpage; ++i) {
                idSpanMap[splitspan->pageid + i] = splitspan;
            }
            spanLists[span->pagenums].PushFront(span);

            return splitspan;
        }
    }

    void *ptr = SystemAlloc(MAX_PAGES - 1);
    memory.push_back(ptr);

    Span *bigspan = pool.New();
    bigspan->pageid = (PAGE_ID)ptr >> PAGE_SHIFT;
    bigspan->pagenums = MAX_PAGES - 1;
    // std::cout << "bigspan->pageid = " << bigspan->pageid << std::endl;

    spanLists[bigspan->pagenums].PushFront(bigspan);

    return NewSpan(numpage);
}


void PageCache::ReleaseSpanToPageCache(Span *span)
{
    // 向前合并
    mtx.lock();
    while (1) {
        PAGE_ID prevPageId = span->pageid - 1;
        auto p = idSpanMap.find(prevPageId);
        if (p == nullptr)
        {
            break;
        }
        Span* prevSpan = p->value;
        if (prevSpan->isUse) {
            break;
        }

        if (span->pagenums + prevSpan->pagenums >= MAX_PAGES) {
            break;
        }

        span->pageid = prevSpan->pageid;
        span->pagenums += prevSpan->pagenums;

        spanLists[prevSpan->pagenums].Erase(prevSpan);
        pool.Delete(prevSpan);
    }

    while (1) {
        PAGE_ID nextPageId = span->pageid + span->pagenums;
        auto p = idSpanMap.find(nextPageId);
        if (p == nullptr)
        {
            break;
        }
        Span* nextSpan = p->value;

        if (nextSpan->isUse) {
            break;
        }

        if (span->pagenums + nextSpan->pagenums >= MAX_PAGES) {
            break;
        }
        span->pagenums += nextSpan->pagenums;

        spanLists[nextSpan->pagenums].Erase(nextSpan);
        pool.Delete(nextSpan);
    }

    spanLists[span->pagenums].PushFront(span);
    span->isUse = false;
    idSpanMap[span->pageid] = span;
    idSpanMap[span->pageid + span->pagenums - 1] = span;
    mtx.unlock();
}

Span *PageCache::GetIdToSpan(PAGE_ID id)
{
    std::lock_guard<std::mutex> lock(mtx);
    auto it = idSpanMap.find(id);
    if (it) return it->value;
    assert(false);
    return nullptr;
}